package com.asggo.g21.db;

import static org.assertj.core.api.Assertions.assertThat;

import cn.hutool.core.date.StopWatch;
import cn.hutool.db.Db;
import cn.hutool.db.ds.simple.SimpleDataSource;
import cn.hutool.json.JSONUtil;
import com.asggo.g21.payload.lann.DemoData;
import com.asggo.g21.utils.RedisUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import java.math.BigDecimal;
import java.sql.SQLException;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.redisson.api.GeoEntry;
import org.redisson.api.GeoPosition;
import org.redisson.api.GeoUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.redisson.api.geo.GeoSearchArgs;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * Created by IntelliJ IDEA.
 *
 * @author eric 2024/1/3 18:28
 */
@SpringBootTest
class RedisTests {

  @Test
  void conn() {
    final RedissonClient client = RedisUtils.getClient();
    assertThat(client).isNotNull();
  }

  final String mobileKey = "g21:mobile";

  @Test
  void cacheMobile() throws SQLException {
    DataSource ds = new SimpleDataSource("jdbc:mysql://localhost:3306/lann", "root", "root");

    final Db db = Db.use(ds);
    final List<String> mobileList = db.query("select mobile from cust_info", String.class);
    final boolean b = RedisUtils.setCacheSet(mobileKey, new HashSet<>(mobileList));

    assertThat(b).isTrue();
  }

  @Test
  void cacheOpenId() throws SQLException {
    DataSource ds = new SimpleDataSource("jdbc:mysql://localhost:3306/lann", "root", "root");

    final Db db = Db.use(ds);
    final List<String> openIdList = db.query(
        "select open_id from weixin_cust_info where open_id is not null", String.class);
    assertThat(openIdList).isNotEmpty();
    Map<String, Integer> openIdStoreMap = new HashMap<>();
    openIdList.forEach(openId -> {
      openIdStoreMap.put(openId, 1);
    });
    RedisUtils.setCacheMap("g21:openId", openIdStoreMap);
  }

  @Test
  void retrieveMobilePhoneNumberFromCache() {
    StopWatch stopWatch = StopWatch.create("1");
    stopWatch.start();
    final List<String> matchMobile = RedisUtils
        .retrieveFromSetCache(mobileKey, "*159599*", 100000, 10);
    stopWatch.stop();
    System.out.println(stopWatch.getTotalTimeMillis());
    assertThat(matchMobile).isNotNull();
    matchMobile.forEach(System.out::println);
  }

  @Test
  void rateLimiter() {
    final String key = "rateLimiter:01";
    int rate = 1;
    int rateInterval = 1;
    long l = RedisUtils.rateLimiter(
        key,
        RateType.OVERALL,
        rate,
        rateInterval
    );
    assertThat(l).isZero();
    l = RedisUtils.rateLimiter(
        key,
        RateType.OVERALL,
        rate,
        rateInterval
    );
    assertThat(l).isEqualTo(-1);
  }

  @Test
  void publish() {
    final String key = "topic:1001";

    RedisUtils.publish(key, "一定没有问题的！");

    assertThat(key).isNotEmpty();
  }

  @Test
  void subscribe() {
    final String key = "topic:1001";
    RedisUtils.subscribe(key, String.class, System.out::println);
    RedisUtils.publish(key, "你是对的");

    assertThat(key).isNotEmpty();
  }

  @Test
  void setCacheObject() {
    final String key = "g21";
    RedisUtils.setCacheObject("g21", "g21");
    DemoData data = new DemoData();
    data.setId(1);
    data.setProdName("abc中文");
    data.setProdAmt(BigDecimal.TEN);
    RedisUtils.setCacheObject("demo:data", data);

    assertThat(key).isNotEmpty();
  }

  @Test
  void setObjectIfAbsent() {
    final String key = "g21";
    final boolean b = RedisUtils
        .setObjectIfAbsent(key, "g22", Duration.ofSeconds(10));
    assertThat(b).isFalse();
  }

  @Test
  void getCacheObject() {
    final String key = "g21";
    final String cacheObject = RedisUtils.getCacheObject(key);
    assertThat(cacheObject).isEqualTo(key);
  }

  @Test
  void cacheCheck() {
    String key = "demo:data";

    final String dataJson = RedisUtils.getCacheObject(key);

    final DemoData bean = JSONUtil.toBean(dataJson, DemoData.class);

    assertThat(bean).isNotNull();
    System.out.println(dataJson);
  }

  @Test
  void geo() {
    String key = "g21:geo";
    RedisUtils.deleteObject(key);

    final long l = RedisUtils.geoAdd(key, 116.404, 39.915, "北京");
    assertThat(l).isEqualTo(1);
    final long l1 = RedisUtils.geoAdd(key,
        new GeoEntry(117.209D, 39.093D, "天津"),
        new GeoEntry(121.480D, 31.235, "上海"),
        new GeoEntry(118.769D, 32.066, "江苏"),
        new GeoEntry(120.21D, 30.253, "浙江"));
    assertThat(l1).isEqualTo(4);

    final Double v = RedisUtils.geoDist(key, "北京", "天津", GeoUnit.KILOMETERS);
    System.out.println("北京 -> 天津 = " + v + "km");

    final List<String> objects = RedisUtils.geoSearch(key, GeoSearchArgs.from(117.404, 39.915)
        .radius(100, GeoUnit.KILOMETERS));

    assertThat(objects).isNotEmpty();
    System.out.println("117.404, 39.915 -> 100km");
    objects.forEach(System.out::println);

    final List<Object> objects1 = RedisUtils.geoSearch(key, GeoSearchArgs.from("上海")
        .radius(500, GeoUnit.KILOMETERS));
    System.out.println("上海 500km");
    assertThat(objects1).isNotEmpty();
    objects1.forEach(System.out::println);

    final Map<String, GeoPosition> city = RedisUtils.geoPos(key, "上海");
    final GeoPosition position = city.get("上海");
    System.out.println(position);
  }

  @Test
  void json() {
    String key = "demo:data:json";
    DemoData data = new DemoData();
    data.setId(1);
    data.setProdName("abc中文json");
    data.setGuid("guid 002");
    data.setProdAmt(BigDecimal.TEN);
    RedisUtils.jsonSet(key, data, DemoData.class);
    final DemoData demoData = RedisUtils.jsonGet(key, DemoData.class);
    assertThat(demoData).isNotNull();

    RedisUtils.jsonSet(key, "$.id", 2, DemoData.class);
    RedisUtils.jsonSet(key, "$.prodName", "abc中文json2", DemoData.class);

    final Object s = RedisUtils.jsonGet(key, new TypeReference<>() {
    }, DemoData.class, "$.prodName", "$.guid");

    assertThat(s).isNotNull();
    System.out.println(s);
  }
}
